/**
 * @name: nanoWIME
 * @author: Janne Schulz (mail@janneschulz.net)
 * @contact: mail@janneschulz.net
 * @url: http://www.janneschulz.net
 * @copyright: Copyright (c) 2010 by Janne Schulz, All rights reserved
 * @license: Artistic License 2.0 ("Perl license"). Please leave this header as it is!
 *
 * @description: nanoWIME stands for "[nano] [WI]ki [M]arkup [E]ditor". 
 *               Its a simple, extensible javascipt Editor for creating Wikimarkup the easy way. You can define
 *               your own buttons and actions, you can modify the icons or do what you want!
 *
 * @credits: The used iconset is called "famfamfam_silk_icons_v013.zip", (Silk icon set 1.3), which is designed by -> Mark James <-
 *           URL: http://www.famfamfam.com/lab/icons/silk
 *           CONTACT: mjames@gmail.com
 *           Great work, thank you!!
 *
 *           My work is also inspired by "tinyMCE", a great, great web based javascript HTML WYSIWYG editor 
 *           URL: http://tinymce.moxiecode.com/
 *
 * @bugs: - (unsolved) (Di 12 Jan 2010 00:33:19 CET) There are some problems under IE with recognizing the shortcuts...
 *
 * @changelog:
 *
 * 0.10.01.22.14: - added a new configuration key to button configuration: 'elementId'
 *
 * 0.10.01.21.23: - some minor changes done (extended buttons configuration in file example_html3.html)
 *
 * 0.10.01.20.18: - major changes: unified the naming of functions (they got a new prefix: '_' - now *every* functionname starts with an '_'-character,
 *                  while configuration-data begins with a normal character. This is done for a better readability and maintainability of the code)
 *
 * 0.10.01.20.15: - added two functions for determining the left and top position of an element
 *
 * 0.10.01.20.01: - better support for inserting new tokens at the end of the text (and repositioning the cursor to that end)
 *
 * 0.10.01.18.17: - added support for more than one button panel
 *
 * 0.10.01.18.00: - rewrote the shortcut-keycode handler to detect the keycode correct
 *                - rewrote the configuration loading handler to add new buttons correctly
 * 
 * 0.10.01.16.21: - modified handling of configuration parameters
 *                - unified configuration keys
 *
 *

 */

 
var nanoWIME = {
    softwareVersion : '0.10.01.22.14',
    iconsPath   : 'images/icons/',
    toolbarId    : 'toolbar',
    toolbarCssClass : 'toolbar',
    shortcutModifier : 17,
    buttonsConf  : {
	h1 : {
	    title : 'Heading 1 [1]',
	    img : 'text_heading_1.png',
	    cssclass : 'button',
	    align : 'middle',
	    on_mouseover : function() {
		this.className = 'raised';
	    },
	    on_mouseout : function() {
		this.className = 'button';
	    },
	    on_mousedown : function() {
		this.className = 'pressed'; 
	    },
	    on_mouseup : function() { 
		this.className = 'raised'; 
	    },
	    on_click : function() {
		nanoWIME._getReplaceText('=', '=');
	    },
	    shortcut : '1'
	},
	h2 : {
	    title : 'Heading 2 [2]',
	    img : 'text_heading_2.png',
	    cssclass : 'button',
	    align : 'middle',
	    on_mouseover : function() {
		this.className = 'raised';
	    },
	    on_mouseout : function() {
		this.className = 'button';
	    },
	    on_mousedown : function() {
		this.className = 'pressed'; 
	    },
	    on_mouseup : function() { 
		this.className = 'raised'; 
	    },
	    on_click : function() {
		nanoWIME._getReplaceText('==', '==');
	    },
	    shortcut : '2'
	},
	h3 : {
	    title : 'Heading 3 [3]',
	    img : 'text_heading_3.png',
	    cssclass : 'button',
	    align : 'middle',
	    on_mouseover : function() {
		this.className = 'raised';
	    },
	    on_mouseout : function() {
		this.className = 'button';
	    },
	    on_mousedown : function() {
		this.className = 'pressed'; 
	    },
	    on_mouseup : function() { 
		this.className = 'raised'; 
	    },	    
	    on_click : function() {
		nanoWIME._getReplaceText('===', '===');
	    },
	    shortcut : '3'
	},
	h4 : {
	    title : 'Heading 4 [4]',
	    img : 'text_heading_4.png',
	    cssclass : 'button',
	    align : 'middle',
	    on_mouseover : function() {
		this.className = 'raised';
	    },
	    on_mouseout : function() {
		this.className = 'button';
	    },
	    on_mousedown : function() {
		this.className = 'pressed'; 
	    },
	    on_mouseup : function() { 
		this.className = 'raised'; 
	    },
	    on_click : function() {
		nanoWIME._getReplaceText('====', '====');
	    },
	    shortcut : '4'
	},
	h5 : {
	    title : 'Heading 5 [5]',
	    img : 'text_heading_5.png',
	    cssclass : 'button',
	    align : 'middle',
	    on_mouseover : function() {
		this.className = 'raised';
	    },
	    on_mouseout : function() {
		this.className = 'button';
	    },
	    on_mousedown : function() {
		this.className = 'pressed'; 
	    },
	    on_mouseup : function() { 
		this.className = 'raised'; 
	    },
	    on_click : function() {
		nanoWIME._getReplaceText('=====', '=====');
	    },
	    shortcut : '5'
	},
	h6 : {
	    title : 'Heading 6 [6]',
	    img : 'text_heading_6.png',
	    cssclass : 'button',
	    align : 'middle',
	    on_mouseover : function() {
		this.className = 'raised';
	    },
	    on_mouseout : function() {
		this.className = 'button';
	    },
	    on_mousedown : function() {
		this.className = 'pressed'; 
	    },
	    on_mouseup : function() { 
		this.className = 'raised'; 
	    },
	    on_click : function() {
		nanoWIME._getReplaceText('======', '======');
	    },
	    shortcut : '6'
	},
	SPACER : {
	    title : 'Spacer',
	    img : 'spacer.png',
	    cssclass : 'spacer_button',
	    align : 'middle'
	},
	ulist : {
	    title : 'Unordered list [b]',
	    img : 'text_list_bullets.png',
	    cssclass : 'button',
	    align : 'middle',
	    on_mouseover : function() {
		this.className = 'raised';
	    },
	    on_mouseout : function() {
		this.className = 'button';
	    },
	    on_mousedown : function() {
		this.className = 'pressed'; 
	    },
	    on_mouseup : function() { 
		this.className = 'raised'; 
	    },
	    on_click : function() {
		nanoWIME._getReplaceText('*', '');
	    },
	    shortcut : 'u'
	},    
	olist : {
	    title : 'Ordered list [o]',
	    img : 'text_list_numbers.png',
	    cssclass : 'button',
	    align : 'middle',
	    on_mouseover : function() {
		this.className = 'raised';
	    },
	    on_mouseout : function() {
		this.className = 'button';
	    },
	    on_mousedown : function() {
		this.className = 'pressed'; 
	    },
	    on_mouseup : function() { 
		this.className = 'raised'; 
	    },
		// modified: For wiky
	    on_click : function() {
		nanoWIME._getReplaceText('1. ', '');
	    },
	    shortcut : 'o'
	},    
	bold : {
	    title : 'Bold [b]',
	    img : 'text_bold.png',
	    cssclass : 'button',
	    align : 'middle',
	    on_mouseover : function() {
		this.className = 'raised';
	    },
	    on_mouseout : function() {
		this.className = 'button';
	    },
	    on_mousedown : function() {
		this.className = 'pressed'; 
	    },
	    on_mouseup : function() { 
		this.className = 'raised'; 
	    },
	    on_click : function() {
		nanoWIME._getReplaceText('\'\'\'', '\'\'\'');
	    },
	    shortcut : 'b'
	},    
	italic : {
	    title : 'Italic [i]',
	    img : 'text_italic.png',
	    cssclass : 'button',
	    align : 'middle',
	    on_mouseover : function() {
		this.className = 'raised';
	    },
	    on_mouseout : function() {
		this.className = 'button';
	    },
	    on_mousedown : function() {
		this.className = 'pressed'; 
	    },
	    on_mouseup : function() { 
		this.className = 'raised'; 
	    },
	    on_click : function() {
		nanoWIME._getReplaceText('\'\'', '\'\'');
	    },
	    shortcut : 'i'
	},    
	strikethrough : {
	    title : 'Strikethrough [s]',
	    img : 'text_strikethrough.png',
	    cssclass : 'button',
	    align : 'middle',
	    on_mouseover : function() {
		this.className = 'raised';
	    },
	    on_mouseout : function() {
		this.className = 'button';
	    },
	    on_mousedown : function() {
		this.className = 'pressed'; 
	    },
	    on_mouseup : function() { 
		this.className = 'raised'; 
	    },
	    on_click : function() {
		nanoWIME._getReplaceText('--', '--');
	    },
	    shortcut : 's'
	},    
	underline : {
	    title : 'Underline [u]',
	    img : 'text_underline.png',
	    cssclass : 'button',
	    align : 'middle',
	    on_mouseover : function() {
		this.className = 'raised';
	    },
	    on_mouseout : function() {
		this.className = 'button';
	    },
	    on_mousedown : function() {
		this.className = 'pressed'; 
	    },
	    on_mouseup : function() { 
		this.className = 'raised'; 
	    },
	    on_click : function() {
		nanoWIME._getReplaceText('##', '##');
	    },
	    shortcut : 'u'
	},    
	sup : {
	    title : 'Superscript',
	    img : 'text_superscript.png',
	    cssclass : 'button',
	    align : 'middle',
	    on_mouseover : function() {
		this.className = 'raised';
	    },
	    on_mouseout : function() {
		this.className = 'button';
	    },
	    on_mousedown : function() {
		this.className = 'pressed'; 
	    },
	    on_mouseup : function() { 
		this.className = 'raised'; 
	    },
	    on_click : function() {
		nanoWIME._getReplaceText('^', '^');
	    }
	},    
	sub : {
	    title : 'Subscript',
	    img : 'text_subscript.png',
	    cssclass : 'button',
	    align : 'middle',
	    on_mouseover : function() {
		this.className = 'raised';
	    },
	    on_mouseout : function() {
		this.className = 'button';
	    },
	    on_mousedown : function() {
		this.className = 'pressed'; 
	    },
	    on_mouseup : function() { 
		this.className = 'raised'; 
	    },
	    on_click : function() {
		nanoWIME._getReplaceText('~', '~');
	    }
	},    
	nowiki : {
	    title : 'NOWIKI Section [n]',
	    img : 'html.png',
	    cssclass : 'button',
	    align : 'middle',
	    on_mouseover : function() {
		this.className = 'raised';
	    },
	    on_mouseout : function() {
		this.className = 'button';
	    },
	    on_mousedown : function() {
		this.className = 'pressed'; 
	    },
	    on_mouseup : function() { 
		this.className = 'raised'; 
	    },
	    on_click : function() {
		nanoWIME._getReplaceText('<nowiki>', '</nowiki>');
	    },
	    shortcut : 'n'
	},    
	link_intern : {
	    title : 'Internernal link [p]',
	    img : 'page_link.png',
	    cssclass : 'button',
	    align : 'middle',
	    on_mouseover : function() {
		this.className = 'raised';
	    },
	    on_mouseout : function() {
		this.className = 'button';
	    },
	    on_mousedown : function() {
		this.className = 'pressed'; 
	    },
	    on_mouseup : function() { 
		this.className = 'raised'; 
	    },
	    on_click : function() {
		nanoWIME._getReplaceText('[[', ']]');
	    },
	    shortcut : 'p'
	},    
	link_extern : {
	    title : 'External link [l]',
	    img : 'world_link.png',
	    cssclass : 'button',
	    align : 'middle',
	    on_mouseover : function() {
		this.className = 'raised';
	    },
	    on_mouseout : function() {
		this.className = 'button';
	    },
	    on_mousedown : function() {
		this.className = 'pressed'; 
	    },
	    on_mouseup : function() { 
		this.className = 'raised'; 
	    },
	    on_click : function() {
		nanoWIME._getReplaceText('[', ']');
	    },
	    shortcut : 'l'
	}
    }, // CONFIG ENDE
    // =====================================================================================================

    _buildPanel : function(panelId, buttonsDefinitionString) {
	var t = nanoWIME;
	var d = document;
	
	var panelObj = d.createElement('div');
	if (t._isDefined(t.toolbarId)) {
	    panelObj.setAttribute('id', t.toolbarId + panelId);
	}
	else {
	    panelObj.setAttribute('id', 'nanoWIMEToolbar' + panelId);
	    t.toolbarId = 'nanoWIMEToolbar';
	} // if ENDE
		
	if (t._isDefined(t.toolbarCssClass)) {
	    panelObj.className = t.toolbarCssClass;
	} // if ENDE
		
	var buttonsArray = buttonsDefinitionString.split(',');
	for (var j = 0; j < buttonsArray.length; j++) {		// >
	    var button = t._trim(buttonsArray[j]);
	    if (button ==  '|') {
		button = 'SPACER';
	    } // if ENDE
	    panelObj = t._addButton(panelObj, button);
	} // for ENDE	
	return panelObj;
    }, // function _buildPanel() ENDE
    // =====================================================================================================

    _buildKeyHandler : function(textAreaObj) {
	// Nun checken wir noch das mit den Shortcuts
	var t = nanoWIME;
	var d = document;
	
	if (t._isDefined(t.shortcutModifier)) {
	    // Ok, es soll auf Shortcuts gehoert werden!
	    t.isModifier = false; 
	    textAreaObj.onkeyup = function(e) {
		var code;
		if (!e) var e = window.event;
		if (e.keyCode) code = e.keyCode;
		else if (e.which) code = e.which;
		
		if (code == t.shortcutModifier) {
		    t.isModifier = false;
		} // if ENDE
	    }; // function ENDE

	    textAreaObj.onkeydown = function(e) { 
	    	var code;
		if (!e) var e = window.event;
		if (e.keyCode) code = e.keyCode;
		else if (e.which) code = e.which;

		if (code == t.shortcutModifier) 
		    t.isModifier = true; 
	
		if (t.isModifier && typeof(t.keyMap[code]) != 'undefined') {
		    t.keyMap[code]();
		    t.isModifier = false;
		    return false;
		} // if ENDE
	    }; // function ENDE
	} // if ENDE
    }, // function _buildKeyHandler() ENDE
    // =====================================================================================================
        
    _internInit : function() {
	var t = nanoWIME;
	var d = document;
	
	t.textarea_obj = d.getElementById(t.elementID); 
	var w = t.textarea_obj.offsetWidth;
	
	for (var i = 1; i < 4; i++) {
	    var btnConf = "buttonBar" + i;
	    if (t._isDefined(t[btnConf])) {
		var buttonsString = t[btnConf];
		var toolbarString = "toolbar" + i;
		t[toolbarString] = t._buildPanel(i, buttonsString);
		
		t[toolbarString].style.width = w + "px";
		t._insertBefore(t[toolbarString], t.textarea_obj);
	    } // if ENDE
	} // for ENDE		
	
	// Nun checken wir noch das mit den Shortcuts
	t._buildKeyHandler(t.textarea_obj);
    }, // function _internInit() ENDE
    // =====================================================================================================

    _insertAfter : function(what, where) {
	where.parentNode.insertBefore(what, where.nextSibling);
    }, // function _insertAfter() ENDE
    // =====================================================================================================    

    _insertBefore : function(what, where) {
	where.parentNode.insertBefore(what, where);
    }, // function _insertBefore() ENDE
    // =====================================================================================================
    
    _trim : function(s) {
	return (s ? '' + s : '').replace(/^\s*|\s*$/g, '');
    }, // function _trim() ENDE
    // =====================================================================================================    

    _getElementLeftPos : function (eElement) {
	if (!eElement && this) {
	    eElement = this;
	} // if ENDE
	var nLeftPos = eElement.offsetLeft;          // initialize var to store calculations
	var eParElement = eElement.offsetParent;     // identify first offset parent element  
	while (eParElement != null) {
	    nLeftPos += eParElement.offsetLeft;      // appending left offset of each parent
	    eParElement = eParElement.offsetParent;  // until no more offset parents exist
	}
	return nLeftPos;                             // return the number calculated
    }, // function _getElementLeftPos() ENDE
    // =====================================================================================================

    _getElementTopPos : function (eElement) {
	if (!eElement && this) {
    	    eElement = this;
	} // if ENDE
		
	var nTopPos = eElement.offsetTop;
	var eParElement = eElement.offsetParent;
	while (eParElement != null) {
	    nTopPos += eParElement.offsetTop;
	    eParElement = eParElement.offsetParent;
	} // while ENDE
        return nTopPos;
    }, // function _getElementTopPos() ENDE
    // =====================================================================================================
    
    _addButton : function(panelObj, buttonType) {
	var t = this;
	var d = document;
	
	var buttonDef = t.buttonsConf[buttonType];
	if (typeof(buttonDef) == 'undefined') {
	    return;
	} // if ENDE
	
	var buttonObj = d.createElement('img');
	if (buttonDef.img.indexOf('/') == 0) {
	    // Es wurde anscheinend ein absoluter Pfad angegeben!
	    buttonObj.src = buttonDef.img;
	}
	else {
	    if (t._isDefined(t.iconsPath)) {
		buttonObj.src = t.iconsPath;
	    } // if ENDE
	    buttonObj.src += buttonDef.img;
	} // if ENDE
	if (t._isDefined(buttonDef.elementId)) {
	    buttonObj.setAttribute('id', buttonDef.elementId);
	} // if ENDE
	if (t._isDefined(buttonDef.title)) {
	    buttonObj.title = buttonDef.title;
	} // if ENDE
	if (t._isDefined(buttonDef.cssclass)) {
	    buttonObj.className = buttonDef.cssclass;
	} // if ENDE
	if (t._isDefined(buttonDef.align)) {
	    buttonObj.align = buttonDef.align;
	} // if ENDE
	if (t._isDefined(buttonDef.on_mouseover)) {
	    buttonObj.onmouseover = buttonDef.on_mouseover;
	} // if ENDE
	if (t._isDefined(buttonDef.on_mouseout)) {
	    buttonObj.onmouseout = buttonDef.on_mouseout;
	} // if ENDE
	if (t._isDefined(buttonDef.on_mousedown)) {
	    buttonObj.onmousedown = buttonDef.on_mousedown;
	} // if ENDE
	if (t._isDefined(buttonDef.on_mouseup)) {
	    buttonObj.onmouseup = buttonDef.on_mouseup;
	} // if ENDE
	if (t._isDefined(buttonDef.on_click)) {
	    buttonObj.onclick = buttonDef.on_click;
	} // if ENDE
	
	if (t._isDefined(buttonDef.shortcut)) {
	    // wir pruefen: ist der Shortcut ein Zeichen oder eine Zahl?
	    if (typeof(buttonDef.shortcut) == 'string') {
		// ok, wir wandeln das zeichen um
		var s = buttonDef.shortcut.toUpperCase();
		var num = s.charCodeAt(0);
		t.keyMap[num] = buttonDef.on_click;
		var shortcutObject = {};
		shortcutObject['shortcut_character'] = buttonDef.shortcut;
		shortcutObject['button_title'] = buttonDef.title;
		t.keyMapSortArray.push(buttonDef.shortcut);
		t.keyMapExtended[buttonDef.shortcut] = shortcutObject;
//		alert("sc: '" + buttonDef.shortcut + "',\ntitle: '" + buttonDef.title + "'");
	    }
	    else {
		t.keyMap[buttonDef.shortcut] = buttonDef.on_click;
	    } // if ENDE
	} // if ENDE

	panelObj.appendChild(buttonObj);
	return panelObj;
    }, // function _addButton() ENDE
    // =====================================================================================================    

    _isDefined : function(obj) {
	if (typeof(obj) == 'undefined') {
	    return false;
	} // if ENDE
	return true;
    }, // function _isDefined() ENDE
    // =====================================================================================================
    
    _getReplaceText : function(beginToken, endToken) {
	var t = this;
	var d = document;
	if (d.selection) {
	    t.textarea_obj.focus();
	    var sel = d.selection.createRange();
	    sel.text = beginToken + sel.text + endToken;
	    return;
	} // if ENDE
	var scrollTop = t.textarea_obj.scrollTop;
	var scrollLeft = t.textarea_obj.scrollLeft;
	
	var len = t.textarea_obj.value.length;
	var start = t.textarea_obj.selectionStart;
	var end = t.textarea_obj.selectionEnd;
	var sel = t.textarea_obj.value.substring(start, end);
	var replace = beginToken + sel + endToken;
	t.textarea_obj.value =  t.textarea_obj.value.substring(0,start) + replace + t.textarea_obj.value.substring(end,len);
	t.textarea_obj.scrollTop = scrollTop;
	t.textarea_obj.scrollLeft = scrollLeft;
		
	t._setSelRange((start + beginToken.length), (start + beginToken.length));
    }, // function _getReplaceText() ENDE
    // ===========================================================================================
    
    _setSelRange : function(selStart, selEnd) { 
	var t = this;
	var d = document;
	if (t.textarea_obj.setSelectionRange) { 
	    t.textarea_obj.focus(); 
	    t.textarea_obj.setSelectionRange(selStart, selEnd); 
	} 
	else if (t.textarea_obj.createTextRange) { 
	    var range = t.textarea_obj.createTextRange(); 
	    range.collapse(true); 
	    range.moveEnd('character', selEnd); 
	    range.moveStart('character', selStart); 
	    range.select(); 
	} // if ENDE
    }, // function _setSelRange() ENDE
    // ===========================================================================================

    _loadConfig : function(confObj) {
	var t = this;
	for (var k in confObj) {
	    if (k == 'buttonsConf') {
		for (var l in confObj[k]) {
		    t[k][l] = confObj[k][l];
		} // for ENDE
	    }
	    else {
		t[k] = confObj[k];
	    } // if ENDE
	} // for ENDE
    }, // function _loadConfig() ENDE
    // ===========================================================================================

    _init : function(conf) {
	// Variablen
	var t = this;
	var d = document;
	t._loadConfig(conf);
	t.keyMap = {};
	t.keyMapExtended = {};
	t.keyMapSortArray = [];
	window.onload = t._internInit;
    } // function _init() ENDE
    // =====================================================================================================
};
